上一份專案我們選用的前端框架是vue,我有提到使用vue的原因是環境設定比較簡單,用官方的cli工具就能夠建立一個完整的專案架構。相較於vue來說,react就沒有那麼方便使用了,在開始寫code之前可能要整理一下檔案結構,然後該裝的套件裝一裝(如node-sass),如果要用redux的話又更麻煩了,連dev tool的環境都要自己設定。所以這邊簡單帶一下如果要在react專案當中使用d3該怎麼寫比較好~
小提醒:我在這邊都會使用function component,所以會大量使用react hook的東西,其實概念很簡單,這邊放上文件https://reactjs.org/docs/hooks-intro.html
在使用create-react-app
建好專案的時候,你會發現他把所有檔案都放在src
的資料夾內,看了實在是不舒服XD,我在這邊介紹一下我習慣的整理方式:
components
的資料夾,把component放進裡面,而一個component也許會有container、component本體、style以及只有這個才會用到的圖片或是js檔案,包成一個資料夾:src
|- components
|- A
|- AContainer.js
|- A.js
|- A.module.scss
|- img
|- someFunc.js
|- B
assets
來放,並從index.js或App.js引入。因為react檔案所有東西都會寫在一起,不像vue那樣template跟function分得好好的,所以我會建議把d3畫圖的function寫在另一個檔案裡面再import進來,否則畫圖的程式碼那麼長,放在component裡面看起來會很亂不好維護。
drawMap.js
import * as d3 from 'd3';
const drawMap = (id) => {
d3
.select(`#${id}`)
.append('svg')
.attr('width', 100)
.attr('height', 100);
}
export default drawMap;
chart.js
import React, { useState, useEffect, useCallback } from 'react';
import shortid from 'shortid';
import drawMap from './drawMap';
const HeatMap = (props) => {
//React hook的用法,等同於以前class component的this.state,在vue當中就是data
const [id] = useState(shortid.generate());
//定義一個畫圖的function,使用useCallback讓id有變動時這個function才會被更新,等同於以前class component裡面定義的this.handleDrawMap(),在vue中則是methods
const handleDrawMap = useCallback(() => {
drawMap(id);
}, [id]);
// 當handleDrawMap有變動時才會執行,而handleDrawMap只有id有變動的時候才會變動,因此目前看起來只有componentDidMount的時候才會重新畫圖
useEffect(() => {
handleDrawMap()
}, [handleDrawMap]);
return <div id={id}></div>;
};
export default React.memo(HeatMap);